﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using ZilLion.Service.Core.ConfigManager.Manager;
using ZilLion.Service.Core.Domain.EntityBase;
using ZilLion.Service.Core.Infrastructure.Base;
using ZilLion.Service.Core.Ioc;
using ZilLion.Service.Core.Log;
using ZilLion.Service.Core.Snowflake;
using ZilLion.Service.Core.WebSocket;
using ZilLion.Service.TaskPlatform.Controllers.StateContainer;
using ZilLion.Service.TaskPlatform.Controllers.Websocket;
using ZilLion.Service.TaskPlatform.Domain;
using ZilLion.Service.TaskPlatform.Domain.Entity;
using ZilLion.Service.TaskPlatform.Domain.ErrorReporter;
using ZilLion.Service.TaskPlatform.Domain.Response;
using ZilLion.Service.TaskPlatform.Domain.Respository.Interface;
using ZilLion.Service.TaskPlatform.Domain.TaskCommand;

namespace ZilLion.Service.TaskPlatform.Controllers
{
    /// <summary>
    ///     任务状态
    /// </summary>
    [Route("Api/TaskPlatform/TaskState")]
    [AllowAnonymous]
    public class TaskStateController : ZilLionControllerBase
    {
        private readonly INodeInfoRespository _nodeInfoRespository;
        private readonly ITaskCommandRespository _taskCommandRespository;
        private readonly ITaskConfigRespository _taskConfigRespository;

        private readonly TaskRunStateContainer _taskRunStateContainer;
        private readonly ITaskStateRespository _taskStateRespository;

        public object statelock = new object();

        public TaskStateController(ITaskCommandRespository taskCommandRespository,
            ITaskStateRespository taskStateRespository, INodeInfoRespository nodeInfoRespository,
            ITaskConfigRespository taskConfigRespository, TaskRunStateContainer taskRunStateContainer)
        {
            _taskCommandRespository = taskCommandRespository;
            _taskStateRespository = taskStateRespository;
            _nodeInfoRespository = nodeInfoRespository;
            _taskConfigRespository = taskConfigRespository;
            _taskRunStateContainer = taskRunStateContainer;
        }

        [HttpPost("UpdateTaskState")]
        public ResponseBase UpdateTaskState([FromBody] TaskStateEntity state)
        {
            ConcurrentDictionary<string, TaskStateEntity> statesdic = null;

            if (_taskRunStateContainer.TaskStates.TryGetValue(state.NodeId, out statesdic))
            {
                if (statesdic != null)
                    statesdic.AddOrUpdate(state.Taskid, state);
            }
            else
            {
                statesdic = new ConcurrentDictionary<string, TaskStateEntity>();
                statesdic.AddOrUpdate(state.Taskid, state);
                _taskRunStateContainer.TaskStates.AddOrUpdate(state.NodeId, statesdic);
            }
            return new ResponseBase();
        }


        /// <summary>
        ///     当命令执行完毕,需要更改任务状态
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        [HttpPost("ReportCommandExcuted")]
        public ResponseBase ReportCommandExcuted([FromBody] TaskConmmandEntity command)
        {
            if (command != null && command.Commandstate == 2)
            {
                _taskCommandRespository.SaveTaskConmmandEntity(command);
                var taskState = _taskStateRespository.GetTaskState(command.Taskid, command.NodeId);
                if (command.Commandname == EnumTaskCommandName.StartTask.ToString())
                    taskState.Taskstatus = 0;
                if (command.Commandname == EnumTaskCommandName.StopTask.ToString())
                    taskState.Taskstatus = 1;
                ConcurrentDictionary<string, TaskStateEntity> statesdic = null;
                _taskRunStateContainer.FlushStates();
                if (_taskRunStateContainer.TaskStates.TryGetValue(command.NodeId, out statesdic))
                {
                    if (statesdic != null)
                        if (statesdic.TryGetValue(command.Taskid, out var state))
                            if (state != null)
                            {
                                state.Taskstatus = taskState.Taskstatus;
                                statesdic.AddOrUpdate(command.Taskid, state);
                                _taskStateRespository.SaveTaskRunState(state);
                            }
                }
                else
                {
                    statesdic = new ConcurrentDictionary<string, TaskStateEntity>();
                    statesdic.AddOrUpdate(taskState.Taskid, taskState);
                    _taskRunStateContainer.TaskStates.AddOrUpdate(command.NodeId, statesdic);
                    _taskStateRespository.SaveTaskRunState(taskState);
                }

                PushStateToOss(taskState);
            }


            return new ResponseBase();
        }


        [HttpPost("ReportTaskExcuted")]
        public ResponseBase ReportTaskExcuted([FromBody] TaskStateEntity state)
        {
            //todo 发现task 状态为停止 但是还在发送成功信息,说明任务停止失败了,要在终端显示错误信息

            if (!_taskRunStateContainer.TaskStates.TryGetValue(state.NodeId, out var statesdic))
                return new ResponseBase();
            if (statesdic == null) return new ResponseBase();
            if (statesdic.TryGetValue(state.Taskid, out var stateout))
            {
                lock (statelock)
                {
                    stateout.TaskExcutedCount = stateout.TaskExcutedCount + 1;
                    stateout.Tasklastruntime = state.Tasklastruntime;
                    stateout.Tasknextruntime = state.Tasknextruntime;
                }
                PushStateToOss(stateout);
            }
            else
            {
                statesdic = new ConcurrentDictionary<string, TaskStateEntity>();
                statesdic.AddOrUpdate(state.Taskid, state);
                _taskRunStateContainer.TaskStates.AddOrUpdate(state.NodeId, statesdic); PushStateToOss(state);
            }
            return new ResponseBase();
        }

        private void PushStateToOss(TaskStateEntity state)
        {
            #region 推送给oss

            IocContainer.Resolve<TaskPlatformWebSocketHandler>()?.SendMessageToGroupAsync(
                JsonConvert.SerializeObject(new SocketKeyDto {ClientNo = "workmanager", ClientType = "taskoss"}),
                new SocketMessage
                {
                    MessageId = SnowflakeIdCreater.Instance().GetNextId().ToString(),
                    Data = JsonConvert.SerializeObject(state),
                    MessageType = "updatetaskstate"
                });

            #endregion
        }

        [HttpGet("ReportTaskError")]
        public ResponseBase ReportTaskError([FromQuery] string taskid, [FromQuery] string nodeid)
        {
            //todo 发现task 状态为停止 但是还在发送成功信息,说明任务停止失败了,需要在终端显示错误信息
            if (!_taskRunStateContainer.TaskStates.TryGetValue(nodeid, out var statesdic)) return new ResponseBase();
            if (statesdic == null) return new ResponseBase();
            if (statesdic.TryGetValue(taskid, out var savedState))
            {
                lock (statelock)
                {
                    savedState.TaskErrorCount = savedState.TaskErrorCount + 1;
                }
                if (savedState.TaskErrorCount >= ZilLionConfigurationManager
                        .GetConfigInstance<TaskManagerServiceConfig>().ErrorStopThreshold)
                {
                    //高于阈值需要停止任务
                    savedState.Taskstatus = 1;
                    TaskCommandHelper.CreateTaskCommand(savedState.NodeId, savedState.Taskid,
                        EnumTaskCommandName.StopTask);

                    _taskStateRespository.SaveTaskRunState(savedState);
                    Task.Run(() =>
                    {
                        try
                        {
                            var reports = ZilLionConfigurationManager
                                .GetConfigInstance<TaskManagerServiceConfig>().ErrorReports;
                            QyWechatHelper.SendMessage(reports,
                                $"节点{savedState.NodeId}的任务{savedState.Taskid}出错超过阈值,已自动停止任务,请知晓");
                        }
                        catch (Exception e)
                        {
                            ZilLionLogManager.Instance().Error(e);
                        }
                    });
                }
                PushStateToOss(savedState);
            }
            else
            {
                statesdic = new ConcurrentDictionary<string, TaskStateEntity>();
                var state = new TaskStateEntity
                {
                    NodeId = nodeid,
                    TaskExcutedCount = 0,
                    TaskErrorCount = 1,
                    Taskid = taskid,
                    Taskstatus = 0
                };
                statesdic.AddOrUpdate(taskid, state);
                _taskRunStateContainer.TaskStates.AddOrUpdate(nodeid, statesdic);
                PushStateToOss(state);
            }

            return new ResponseBase();
        }


        /// <summary>
        ///     获取任务执行状态
        /// </summary>
        /// <param name="nodeid"></param>
        /// <returns></returns>
        [HttpGet("GetAllTaskState")]
        public TaskStateResponse GetAllTaskState(string nodeid = null)
        {
            var list = _taskStateRespository.GetAllTaskState(nodeid);
            return new TaskStateResponse {TaskStates = list};
        }


        /// <summary>
        ///     获取任务执行状态
        /// </summary>
        /// <param name="nodeid"></param>
        /// <returns></returns>
        [HttpGet("GetNodeTask")]
        public TaskStateDtoResponse GetNodeTask(string nodeid)
        {
            var dtos = new List<TaskStateDto>();

            var states = _taskStateRespository.GetAllTaskState(nodeid);
            var node = _nodeInfoRespository.GetNodeInfoByNodeId(nodeid);

            foreach (var state in states)
            {
                var dto = new TaskStateDto
                {
                    TaskState = state,
                    NodeInfo = node,
                    TaskConfig = _taskConfigRespository.GetjobConfigById(state.Taskid) ?? new TaskConfig()
                };
                dtos.Add(dto);
            }
            return new TaskStateDtoResponse {TaskStates = dtos};
        }

        /// <summary>
        ///     获取任务执行状态
        /// </summary>
        /// <param name="taskid"></param>
        /// <param name="nodeid"></param>
        /// <returns></returns>
        [HttpGet("GetTaskState")]
        public TaskStateResponse GetTaskStateById([FromQuery] string taskid, [FromQuery] string nodeid)
        {
            var list = _taskStateRespository.GetTaskState(taskid, nodeid);
            return new TaskStateResponse {TaskStates = new List<TaskStateEntity> {list}};
        }

        /// <summary>
        ///     更新任务状态
        /// </summary>
        /// <param name="stateEntitys"></param>
        /// <param name="nodeid"></param>
        /// <returns></returns>
        [HttpPost("UpdateNodeTaskStates")]
        public ResponseBase UpdateNodeTaskStates([FromBody] IList<TaskStateEntity> stateEntitys,
            [FromQuery] string nodeid)
        {
            foreach (var state in stateEntitys)
            {
                var task = _taskConfigRespository.GetjobConfigById(state.Taskid);
                state.Taskname = task?.Taskname ?? string.Empty;
            }

            ConcurrentDictionary<string, TaskStateEntity> statesdic = null;

            if (_taskRunStateContainer.TaskStates.TryGetValue(nodeid, out statesdic))
            {
                foreach (var state in stateEntitys)
                {
                    if (!statesdic.ContainsKey(state.Taskid))
                        statesdic.AddOrUpdate(state.Taskid, state);

                    _taskStateRespository.SaveTaskRunState(state);
                }
            }
            else
            {
                statesdic = new ConcurrentDictionary<string, TaskStateEntity>();
                foreach (var state in stateEntitys)
                {
                    if (!statesdic.ContainsKey(state.Taskid))
                        statesdic.AddOrUpdate(state.Taskid, state);
                    _taskRunStateContainer.TaskStates.AddOrUpdate(nodeid, statesdic);
                    _taskStateRespository.SaveTaskRunState(state);
                }
            }


            return new ResponseBase();
        }
    }
}